//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

#include "pch.h"
#include "Scenario5_RestoreScrollPosition.h"
#include "Scenario5_RestoreScrollPosition.g.cpp"

using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;

namespace winrt::SDKTemplate::implementation
{
    double Scenario5_RestoreScrollPosition::_persistedItemContainerHeight = -1.0;
    hstring Scenario5_RestoreScrollPosition::_persistedItemKey;
    hstring Scenario5_RestoreScrollPosition::_persistedPosition;

    Scenario5_RestoreScrollPosition::Scenario5_RestoreScrollPosition()
    {
        InitializeComponent();
    }

    fire_and_forget Scenario5_RestoreScrollPosition::Page_Loaded(IInspectable const&, RoutedEventArgs const& e)
    {
        if (!_persistedPosition.empty())
        {
            // Here we kick off the async function to use the saved string _persistedPosition and the function GetItem to restore the scroll posistion
            auto lifetime = get_strong();
            co_await ListViewPersistenceHelper::SetRelativeScrollPositionAsync(ItemsListView(), _persistedPosition, { this, &Scenario5_RestoreScrollPosition::GetItem });
        }
    }

    void Scenario5_RestoreScrollPosition::OnNavigatingFrom(NavigatingCancelEventArgs const&)
    {
        // Before we navigate away from the page, we want to generate a new _persistedPosition string. This string contains the key of the item at the top of the 
        // viewing window (generated by the function GetKey) and the offset of that item.
        _persistedPosition = ListViewPersistenceHelper::GetRelativeScrollPosition(ItemsListView(), { this, &Scenario5_RestoreScrollPosition::GetKey });
    }

    IAsyncOperation<IInspectable> Scenario5_RestoreScrollPosition::GetItem(hstring const& key)
    {
        // This function takes a key that ListViewPersistenceHelper parsed out of the _persistedPosition string
        // It returns an item that corresponds to the given key
        // The implementation of this function is dependent on the data model you are using. Every item in your list should have
        // a unique key this function returns
        auto found = std::find_if(items.begin(), items.end(), [&](auto&& item) { return item.Id() == key; });
        co_return found == items.end() ? nullptr : *found;
    }

    hstring Scenario5_RestoreScrollPosition::GetKey(IInspectable const& object)
    {
        // This function takes in the item at the top of the viewport at the moment of navigating away from the page, and returns
        // a key corresponding to that item. the implementation of this function is dependent on the data model you are using. In this 
        // function we also save the fully rendered _persistedItemContainerHeight. You do not need to do this if all of your items have 
        // the same fixed height. Finally, we save the key into a variable so it can be used outside of ListViewPersistenceHelper functions
        SDKTemplate::Item item = object.try_as<SDKTemplate::Item>();
        if (item != nullptr)
        {
            _persistedItemContainerHeight = ItemsListView().ContainerFromItem(item).as<ListViewItem>().ActualHeight();
            _persistedItemKey = item.Id();
            return _persistedItemKey;
        }
        else
        {
            return L"";
        }
    }

    void Scenario5_RestoreScrollPosition::ItemsListView_ContainerContentChanging(ListViewBase const&, ContainerContentChangingEventArgs const& args)
    {
        // This function manually sets the height of the item ListViewPersistenceHelper is attempting to scroll to. We need to set the height
        // because if the item is not fully rendered at the time of scrolling, it can return an incorrect height and cause ListViewPersistenceHelper 
        // to overscroll. 
        SDKTemplate::Item item = args.Item().try_as<SDKTemplate::Item>();

        if (item != nullptr && item.Id() == _persistedItemKey)
        {
            if (!args.InRecycleQueue())
            {
                // Here we set the container's height equal to the fully rendered container height we had saved before navigating away. If all the items in 
                // your list have the same fixed height, you can replace this variable with a hardcoded height value. 
                args.ItemContainer().Height(_persistedItemContainerHeight);
            }
            else
            {
                // Containers in a list are recycled when they are scrolled out of view. However, if those containers have their Height property set and the content
                // changes, that set Height is still applied. This creates an incorect UI if the items in your list are supposed to be of variable height. 
                // If all the items in your list have the same fixed height, you do not have to do this. 
                args.ItemContainer().ClearValue(FrameworkElement::HeightProperty());
            }
        }
    }

    void Scenario5_RestoreScrollPosition::ItemsListView_ItemClick(IInspectable const&, RoutedEventArgs const&)
    {
        Frame().Navigate(xaml_typename<SDKTemplate::ImagePage>());
    }

}
